package com.niiwoo.civet.trade.service.local.newRefund.refundDeal.core;

import com.alibaba.dubbo.config.annotation.Reference;
import com.niiwoo.asset.org.business.enums.org.FreezeOrderStatusEnum;
import com.niiwoo.civet.account.dto.request.TransFreezeRequestDTO;
import com.niiwoo.civet.account.dto.response.TransFreezeResponseDTO;
import com.niiwoo.civet.account.enums.TransFreezeStatusEnum;
import com.niiwoo.civet.account.service.trade.TransOrderDubboService;
import com.niiwoo.civet.base.dto.BaseFeeDetailDTO;
import com.niiwoo.civet.base.enums.BorrowProjectTypeEnum;
import com.niiwoo.civet.base.enums.CommonResultEnum;
import com.niiwoo.civet.base.enums.FeeTypeEnum;
import com.niiwoo.civet.trade.constant.StaticConstant;
import com.niiwoo.civet.trade.constant.TradeMqConstant;
import com.niiwoo.civet.trade.dao.entity.RefundOrder;
import com.niiwoo.civet.trade.dao.mapper.RefundOrderMapperExt;
import com.niiwoo.civet.trade.dto.newRefund.RefundOrderDetailDTO;
import com.niiwoo.civet.trade.enums.RefundTradeTypeEnum;
import com.niiwoo.civet.trade.service.local.newRefund.notify.dto.RefundNotifyMsgDTO;
import com.niiwoo.civet.trade.service.local.newRefund.notify.enums.RefundNotifyRefundStatusEnum;
import com.niiwoo.civet.trade.service.local.newRefund.refundDeal.converter.RefundEnumConverter;
import com.niiwoo.tripod.lanmao.component.LanMaoSequence;
import com.niiwoo.tripod.provider.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

/**
 * 冻结结果处理
 */
@Service
@Slf4j
public class FreezeDealingService {

    @Autowired
    private LanMaoSequence lanMaoSequence;

    @Reference(version = "1.0.0")
    private TransOrderDubboService transOrderDubboService;

    @Autowired
    private RefundOrderMapperExt refundOrderMapperExt;

    @Autowired
    private RefundAllotFeeForGuaranteeService refundAllotFeeForGuaranteeService;

    @Autowired
    private RefundAllotFeeForRepaymentService refundAllotFeeForRepaymentService;

    @Autowired
    private RefundToLanMaoService refundToLanMaoService;
    
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 冻结后置处理
     */
    public void freezeDealBatch(){
        log.info("还款冻结补单批处理start");
        List<RefundOrder> list = refundOrderMapperExt.selectFreezeDealingList();
        FreezeDealingService freezeDealingService = (FreezeDealingService)AopContext.currentProxy();
        for (RefundOrder refundOrder : list) {
            try{
                freezeDealingService.freezeDeal(refundOrder.getId());
            } catch (Exception e){
                log.error("还款查询冻结处理失败job，refundOrderId={}", refundOrder.getId(), e);
            }
        }
        log.info("还款冻结补单批处理end");
    }

    /**
     * 冻结后置处理
     * @param refundOrderId
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
    public void  freezeDeal(Long refundOrderId){
        RefundOrder refundOrderForUpdate = refundOrderMapperExt.selectByPrimaryKeyForUpdate(refundOrderId);
        if(FreezeOrderStatusEnum.PROCESSING.getStatus().equals(refundOrderForUpdate.getFreezeStatus())){
            Date now = new Date();
            TransFreezeResponseDTO transFreezeResponseDTO = null;
            try{
                transFreezeResponseDTO = transOrderDubboService.queryTransFreeze(refundOrderId, RefundEnumConverter.orderTypeEnumConvert(refundOrderForUpdate.getTradeType()));
            } catch (Exception e){
                if(e instanceof BizException
                        &&  "ACC31001".equals(((BizException)e).getErrorCode())
                        && new DateTime(refundOrderForUpdate.getCreateTime()).plusMinutes(10).toDate().before(now)){
                    log.error("还款冻结订单不存在，时间已到，直接失败，refundOrderId=" + refundOrderId);
                    this.freezeFail(refundOrderForUpdate, "-1", "冻结订单不存在");
                    return;
                }else{
                    log.error("还款冻结订单查询异常，refundOrderId=" + refundOrderId, e);
                    throw e;
                }
            }
            CommonResultEnum freezeResult = null;
            TransFreezeStatusEnum transFreezeStatusEnum = transFreezeResponseDTO.getTransFreezeStatusEnum();
            if(TransFreezeStatusEnum.SUCCESS.equals(transFreezeStatusEnum)){
                freezeResult = CommonResultEnum.SUCCESS;
            }else if(TransFreezeStatusEnum.FAIL.equals(transFreezeStatusEnum)){
                freezeResult = CommonResultEnum.FAILURE;
            }
            String freezeRequestNo = transFreezeResponseDTO.getFreezeRequestNo();
            this.freezeDeal(refundOrderId, freezeResult, freezeRequestNo, refundOrderForUpdate, transFreezeResponseDTO.getErrorCode(), transFreezeResponseDTO.getErrorMessage());
        }
    }

    /**
     * 冻结后置处理
     * @param refundOrderId
     * @param freezeResult
     * @param freezeRequestNo
     * @param refundOrderForUpdate
     * @param errorCode
     * @param errorMessage
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
    public void  freezeDeal(Long refundOrderId, CommonResultEnum freezeResult, String freezeRequestNo, RefundOrder refundOrderForUpdate, String errorCode, String errorMessage){
        try{
            if(null == refundOrderForUpdate){
                refundOrderForUpdate = refundOrderMapperExt.selectByPrimaryKeyForUpdate(refundOrderId);
            }
            if(FreezeOrderStatusEnum.PROCESSING.getStatus().equals(refundOrderForUpdate.getFreezeStatus())){
                if(CommonResultEnum.SUCCESS.equals(freezeResult)){// 成功处理
                    // 冻结成功发起下一个流程（配帐，发起存管还款/垫付批量请求）
                    refundOrderForUpdate.setFreezeRequestNo(freezeRequestNo);
                    refundOrderForUpdate.setBatchNo(lanMaoSequence.getBatchNo());
                    refundOrderForUpdate.setFreezeStatus(FreezeOrderStatusEnum.SUCCESS.getStatus());
                    refundOrderForUpdate.setFreezedTime(new Date());
                    refundOrderForUpdate.setStatus(Byte.valueOf("1"));
                    refundOrderForUpdate.setUpdateTime(new Date());
                    refundOrderMapperExt.updateByPrimaryKeySelective(refundOrderForUpdate);
                    // 分配金额
                    List<RefundOrderDetailDTO> refundOrderDetailDTOs;
                    if (RefundTradeTypeEnum.ADVANCE.getTradeType().equals(refundOrderForUpdate.getTradeType())) {// 垫付分配资金
                        refundOrderDetailDTOs = refundAllotFeeForGuaranteeService.allotFee(refundOrderForUpdate);
                    } else {// 还款、代还分配资金
                        refundOrderDetailDTOs = refundAllotFeeForRepaymentService.allotFee(refundOrderForUpdate);
                    }
                    final List<RefundOrderDetailDTO> finalRefundOrderDetailDTOs = refundOrderDetailDTOs;
                    final String fianlBatchNo = refundOrderForUpdate.getBatchNo();
                    final Long finalRefundOrderId = refundOrderId;
                    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                        @Override
                        public void afterCommit() {
                            // 调起存管
                            refundToLanMaoService.refundToLanMao(finalRefundOrderDetailDTOs, fianlBatchNo, finalRefundOrderId);
                        }
                    });
                } else if(CommonResultEnum.FAILURE.equals(freezeResult)){// 失败处理
                    this.freezeFail(refundOrderForUpdate, errorCode, errorMessage);
                }
            }
        } catch (Exception e){
            log.error("还款查询冻结处理失败，refundOrderId={}", refundOrderId, e);
            throw e;
        }
    }

    /**
     * 冻结失败处理 直接处理成失败,不需要处理其他流程
     * @param refundOrder
     */
    private void freezeFail(RefundOrder refundOrder, String errorCode, String errorMessage){
        RefundOrder refundOrderUpdater = new RefundOrder();
        refundOrderUpdater.setId(refundOrder.getId());
        refundOrderUpdater.setFreezeStatus(FreezeOrderStatusEnum.FAILED.getStatus());
        refundOrderUpdater.setProcessStatus(Byte.valueOf("2"));
        refundOrderUpdater.setErrorCode(errorCode);
        refundOrderUpdater.setErrorMessage(errorMessage);
        refundOrderUpdater.setOverTime(new Date());
        refundOrderUpdater.setUpdateTime(new Date());
        refundOrderMapperExt.updateByPrimaryKeySelective(refundOrderUpdater);
        
        // 还款通知MQ消息
    	RefundNotifyMsgDTO refundNotifyMsgDTO = new RefundNotifyMsgDTO();
    	refundNotifyMsgDTO.setRefundStatus(RefundNotifyRefundStatusEnum.REPAPYMENT_FAIL.getStatus());
		refundNotifyMsgDTO.setProjectId(refundOrder.getProjectId());
		refundNotifyMsgDTO.setRefundOrderId(refundOrder.getId());
		refundNotifyMsgDTO.setOrgRequestNo(refundOrder.getOrgRequestNo());
		refundNotifyMsgDTO.setRefundTradeSource(refundOrder.getTradeSource());
		refundNotifyMsgDTO.setFailMessage(errorMessage);
		rabbitTemplate.convertAndSend(TradeMqConstant.REFUND_NOTIFY_HANDLE_EXCHANGE,
				TradeMqConstant.REFUND_NOTIFY_HANDLE_ROUTINGKEY, refundNotifyMsgDTO);
    }

    /**
     * 构建请求参数
     * @param refundOrder
     * @param projectType
     * @param projectTitle
     * @param currentPeriod
     * @param totalPeriods
     * @return
     */
    public TransFreezeRequestDTO buidTransFreezeRequestDTO(RefundOrder refundOrder, Byte projectType, String projectTitle,
                                                           Integer currentPeriod, Integer totalPeriods){
        List<BaseFeeDetailDTO> feeDetailDTOList = new LinkedList<>();
        TransFreezeRequestDTO result = new TransFreezeRequestDTO();
        result.setFeeDetailDTOList(feeDetailDTOList);
        result.setOrderId(refundOrder.getId());
        result.setOrderType(RefundEnumConverter.orderTypeEnumConvert(refundOrder.getTradeType()));
        result.setOrderRefundTypeEnum(RefundEnumConverter.orderRefundTypeEnumConvert(refundOrder.getRefundType(),refundOrder.getTradeType(), refundOrder.getRepayStatus()));
        result.setProjectId(refundOrder.getProjectId());
        result.setBorrowProjectTypeEnum(BorrowProjectTypeEnum.enumOf(projectType));
        result.setProjectTitle(projectTitle);
        result.setCurrentPeriod(currentPeriod);
        result.setTotalPeriods(totalPeriods);
        result.setRepaymentAccountId(refundOrder.getSourceAccountId());
        result.setAmount(refundOrder.getAmount());
        result.setExpireTime(new DateTime().plusMinutes(StaticConstant.REFUND_FREZEE_EXPIRE_MINUTES).toDate());
        result.setRefundOrderCreateTime(refundOrder.getCreateTime());
        if(refundOrder.getCapital().compareTo(BigDecimal.ZERO) > 0){
            BaseFeeDetailDTO dto = new BaseFeeDetailDTO();
            dto.setFeeTypeEnum(FeeTypeEnum.CAPITAL);
            dto.setAmount(refundOrder.getCapital());
            feeDetailDTOList.add(dto);
        }

        if(refundOrder.getInterest().compareTo(BigDecimal.ZERO) > 0){
            BaseFeeDetailDTO dto = new BaseFeeDetailDTO();
            dto.setFeeTypeEnum(FeeTypeEnum.INTEREST);
            dto.setAmount(refundOrder.getInterest());
            feeDetailDTOList.add(dto);
        }

        if(refundOrder.getPenaltyFee().compareTo(BigDecimal.ZERO) > 0){
            BaseFeeDetailDTO dto = new BaseFeeDetailDTO();
            dto.setFeeTypeEnum(FeeTypeEnum.PENALTY);
            dto.setAmount(refundOrder.getPenaltyFee());
            feeDetailDTOList.add(dto);
        }

        if(refundOrder.getGuaranteeFee().compareTo(BigDecimal.ZERO) > 0){
            BaseFeeDetailDTO dto = new BaseFeeDetailDTO();
            dto.setFeeTypeEnum(FeeTypeEnum.GUARANTEE_FEE);
            dto.setAmount(refundOrder.getGuaranteeFee());
            feeDetailDTOList.add(dto);
        }

        if(refundOrder.getManageFee().compareTo(BigDecimal.ZERO) > 0){
            BaseFeeDetailDTO dto = new BaseFeeDetailDTO();
            dto.setFeeTypeEnum(FeeTypeEnum.MANAGEMENT_FEE);
            dto.setAmount(refundOrder.getManageFee());
            feeDetailDTOList.add(dto);
        }

        if(refundOrder.getConsultingFee().compareTo(BigDecimal.ZERO) > 0){
            BaseFeeDetailDTO dto = new BaseFeeDetailDTO();
            dto.setFeeTypeEnum(FeeTypeEnum.CONSULTING_FEE);
            dto.setAmount(refundOrder.getConsultingFee());
            feeDetailDTOList.add(dto);
        }
        return result;
    }

}
